package cn.iocoder.yudao.framework.mybatis.core.mapper;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
import cn.iocoder.yudao.framework.common.pojo.SortingField;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.enums.QueryCondition;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.github.yulichang.base.MPJBaseMapper;
import com.github.yulichang.interfaces.MPJBaseJoin;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.ibatis.annotations.Param;

import java.util.*;

/**
 * 在 MyBatis Plus 的 BaseMapper 的基础上拓展，提供更多的能力
 * <p>
 * 1. {@link BaseMapper} 为 MyBatis Plus 的基础接口，提供基础的 CRUD 能力
 * 2. {@link MPJBaseMapper} 为 MyBatis Plus Join 的基础接口，提供连表 Join 能力
 */
public interface BaseMapperX<T> extends MPJBaseMapper<T> {

    default MPJLambdaWrapperX<T> buildQueryMapper(PageParam pageParam) {
        MPJLambdaWrapperX<T> queryWrapper = new MPJLambdaWrapperX<>();
        Map<String, Map<String, Object>> params = new HashMap<>(pageParam.getParams());
        // 移除非本表字段的key, 然后在外层单独处理这个key
        params.remove("label_id");
        params.remove("pool");
        // 遍历这个params
        if (CollUtil.isEmpty(params)) {
            return queryWrapper;
        }
        params.forEach((key, value) -> {

            Object val = value.get("value");
            if ("create_time".equals(key)) {
                key = "DATE_FORMAT (t.create_time,'%Y-%m-%d %H:%i:%s')";
            }
            String condition = value.get("condition").toString();
            switch (condition) {
                case QueryCondition.EQ:
                    queryWrapper.eq(key, val);
                    break;
                case QueryCondition.NEQ:
                    queryWrapper.ne(key, val);
                    break;
                case QueryCondition.IN:
                    List<Object> list1 = JSONUtil.toList(JSONUtil.parseArray(val), Object.class);
                    queryWrapper.in(key, list1);
                    break;
                case QueryCondition.NIN:
                    queryWrapper.notIn(key, val);
                    break;
                case QueryCondition.LIKE:
                    queryWrapper.like(key, val);
                    break;
                case QueryCondition.GT:
                    queryWrapper.gt(key, val);
                    break;
                case QueryCondition.GTE:
                    queryWrapper.ge(key, val);
                    break;
                case QueryCondition.LT:
                    queryWrapper.lt(key, val);
                    break;
                case QueryCondition.LTE:
                    queryWrapper.le(key, val);
                    break;
                case QueryCondition.BETWEEN:
                    List<Object> list = JSONUtil.toList(JSONUtil.parseArray(val), Object.class);
                    queryWrapper.between(key, list.get(0), list.get(1));
                    break;
                case QueryCondition.IS_NOT_NULL:
                    queryWrapper.isNotNull(key);
                    break;
                case QueryCondition.IS_NULL:
                    queryWrapper.isNull(key);
                    break;
            }
        });

        return queryWrapper;
    }

    default PageResult<T> selectPage(SortablePageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
        return selectPage(pageParam, pageParam.getSortingFields(), queryWrapper);
    }

    default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
        return selectPage(pageParam, null, queryWrapper);
    }

    default PageResult<T> selectPage(PageParam pageParam, Collection<SortingField> sortingFields, @Param("ew") Wrapper<T> queryWrapper) {
        // 特殊：不分页，直接查询全部
        if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
            List<T> list = selectList(queryWrapper);
            return new PageResult<>(list, (long) list.size());
        }

        // MyBatis Plus 查询
        IPage<T> mpPage = MyBatisUtils.buildPage(pageParam, sortingFields);
        selectPage(mpPage, queryWrapper);
        // 转换返回
        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
    }

    default <D> PageResult<D> selectJoinPage(PageParam pageParam, Class<D> clazz, MPJLambdaWrapper<T> lambdaWrapper) {
        // 特殊：不分页，直接查询全部
        if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageNo())) {
            List<D> list = selectJoinList(clazz, lambdaWrapper);
            return new PageResult<>(list, (long) list.size());
        }

        // MyBatis Plus Join 查询
        IPage<D> mpPage = MyBatisUtils.buildPage(pageParam);
        mpPage = selectJoinPage(mpPage, clazz, lambdaWrapper);
        // 转换返回
        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
    }

    default <DTO> PageResult<DTO> selectJoinPage(PageParam pageParam, Class<DTO> resultTypeClass, MPJBaseJoin<T> joinQueryWrapper) {
        IPage<DTO> mpPage = MyBatisUtils.buildPage(pageParam);
        selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper);
        // 转换返回
        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
    }

    default T selectOne(String field, Object value) {
        return selectOne(new QueryWrapper<T>().eq(field, value));
    }

    default T selectOne(SFunction<T, ?> field, Object value) {
        return selectOne(new LambdaQueryWrapper<T>().eq(field, value));
    }

    default T selectOne(String field1, Object value1, String field2, Object value2) {
        return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2));
    }

    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
    }

    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
                        SFunction<T, ?> field3, Object value3) {
        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
                .eq(field3, value3));
    }

    default Long selectCount() {
        return selectCount(new QueryWrapper<>());
    }

    default Long selectCount(String field, Object value) {
        return selectCount(new QueryWrapper<T>().eq(field, value));
    }

    default Long selectCount(SFunction<T, ?> field, Object value) {
        return selectCount(new LambdaQueryWrapper<T>().eq(field, value));
    }

    default List<T> selectList() {
        return selectList(new QueryWrapper<>());
    }

    default List<T> selectList(String field, Object value) {
        return selectList(new QueryWrapper<T>().eq(field, value));
    }

    default List<T> selectList(SFunction<T, ?> field, Object value) {
        return selectList(new LambdaQueryWrapper<T>().eq(field, value));
    }

    default List<T> selectList(String field, Collection<?> values) {
        if (CollUtil.isEmpty(values)) {
            return CollUtil.newArrayList();
        }
        return selectList(new QueryWrapper<T>().in(field, values));
    }

    default List<T> selectList(SFunction<T, ?> field, Collection<?> values) {
        if (CollUtil.isEmpty(values)) {
            return CollUtil.newArrayList();
        }
        return selectList(new LambdaQueryWrapper<T>().in(field, values));
    }

    @Deprecated
    default List<T> selectList(SFunction<T, ?> leField, SFunction<T, ?> geField, Object value) {
        return selectList(new LambdaQueryWrapper<T>().le(leField, value).ge(geField, value));
    }

    default List<T> selectList(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
        return selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
    }

    /**
     * 批量插入，适合大量数据插入
     *
     * @param entities 实体们
     */
    default Boolean insertBatch(Collection<T> entities) {
        return Db.saveBatch(entities);
    }

    /**
     * 批量插入，适合大量数据插入
     *
     * @param entities 实体们
     * @param size     插入数量 Db.saveBatch 默认为 1000
     */
    default Boolean insertBatch(Collection<T> entities, int size) {
        return Db.saveBatch(entities, size);
    }

    default int updateBatch(T update) {
        return update(update, new QueryWrapper<>());
    }

    default Boolean updateBatch(Collection<T> entities) {
        return Db.updateBatchById(entities);
    }

    default Boolean updateBatch(Collection<T> entities, int size) {
        return Db.updateBatchById(entities, size);
    }

    default Boolean insertOrUpdate(T entity) {
        return Db.saveOrUpdate(entity);
    }

    default Boolean insertOrUpdateBatch(Collection<T> collection) {
        return Db.saveOrUpdateBatch(collection);
    }

    default int delete(String field, String value) {
        return delete(new QueryWrapper<T>().eq(field, value));
    }

    default int delete(SFunction<T, ?> field, Object value) {
        return delete(new LambdaQueryWrapper<T>().eq(field, value));
    }


    default void buildSortFields(PageParam pageParam, MPJLambdaWrapperX<T> query) {
        // 拼接排序条件
        String str = pageParam.getSortStr();
        if (StrUtil.isNotEmpty(str)) {
            JSONObject jsonObject = JSONUtil.parseObj(str);
            jsonObject.forEach((key, value) -> {
                if (Objects.equals(value, "asc")) {
                    query.orderByAsc(convertKey(key));
                } else if (Objects.equals(value, "desc")) {
                    query.orderByDesc(convertKey(key));
                }
            });
        }

    }


    // TODO: 这里后面还是封装在各自的实现类中比较好.
    default String convertKey(String key) {
        switch (key) {
            case "industryId":
                return "industry_id";
            case "contactLastTime":
                return "contact_last_time";
            case "contactNextTime":
                return "contact_next_time";
            case "createTime":
                return "create_time";
            case "updateTime":
                return "update_time";
            case "dealStatus":
                return "deal_status";
            case "followUpStatus":
                return "follow_up_status";
            case "lockStatus":
                return "lock_status";

            case "transformStatus":
                return "transform_status";
            case "totalPrice":
                return "total_price";
            case "dealTime":
                return "deal_time";
            case "statusName":
                return "status_id";
            case "orderDate":
                return "order_date";
            case "startTime":
                return "start_time";
            case "endTime":
                return "end_time";
            case "auditStatus":
                return "audit_status";

            case "returnTime":
                return "return_time";

            case "remindTime":
                return "remind_time";
            case "callSecond":
                return "call_second";
            case "callMinute":
                return "call_minute";
            case "downloadStatus":
                return "download_status";
            case "resultType":
                return "result_type";

            case "poolDay":
                return "pool_day";
                // 线索、客户 进入公海时间
            case "intoPoolTime":
                return "into_pool_time";
            // !fixed：线索新增的字段 dataType 需要转成 data_type 来排序
            case "dataType":
                return "data_type";
            // !fixed: 增加领取时间
            case "ownerTime":
                return "owner_time";

            // !fixed 增加客户级别排序
            case "levelValue":
                return "level_value";
        }
        return key;
    }
}
